'''
Created on Feb 7, 2012

@author: cgiacofe



'''

from xml.etree import ElementTree as ET
import time
import math
import random
import urllib2
from sets import Set


import constants
import logging

# XML tags
SERVER = 'server'
UPTIME = 'uptime'
ISP_IP = 'isp_ip'
VPN_IP = 'vpn_ip'
TIMESTAMP = 'timestamp'
START = 'start_time'
END = 'end_time'
ERROR = 'error'
VERSION = 'version'
SCORE = 'score'

class Ddict(dict):
    def __init__(self, default=None):
        self.default = default

    def __getitem__(self, key):
        if not self.has_key(key):
            self[key] = self.default()
        return dict.__getitem__(self, key)

def compare_lists(servers, stat_list):
    '''
    Return a list of servers that have not been run yet
    '''
    stat_servers = tuple(x[0] for x in stat_list)
    
    return Set(servers) - Set(stat_servers)
    
    
def download_servers(list_url):
    try:
        server_list = urllib2.urlopen(list_url).read().split('\n')
    except:
        server_list = 'failed'
    
    return server_list

def weighted_sample(items, n):
    '''
    Takes a list of weighted options and returns a random item::
        items = [(10, "low"),
             (100, "mid"),
             (890, "large")]
    
    @param items: The list of items to choose from
    @param n: The number of items to pull from the list
    
    Code sample pulled from: 
    http://stackoverflow.com/questions/2140787/select-random-k-elements-from-a-list-whose-elements-have-weights
    '''
    total = float(sum(w for w, v in items))
    i = 0
    w, v = items[0]
    while n:
        x = total * (1 - random.random() ** (1.0 / n))
        total -= x
        while x > w:
            x -= w
            i += 1
            w, v = items[i]
        w -= x
        yield v
        n -= 1
        
def get_server_scores(filename):
    '''
    Pulls server names and scores from the stats file and returns as a list of tuples.
    '''
    root = ET.parse(filename).getroot()
    
    server_list = []
    
    for subelement in root:
        server = subelement.find(SERVER).text
        score = float(subelement.find(SCORE).text)
        
        # There must be a better way!!!
        server = server.strip('\n')
        server = server.strip('\t')
        server = server.strip('\n')
        
        server_list.append((score,server))
    
    return server_list
    
def calc_stats(filename):
    '''
    Calculate server statistics from the XML logfile
    
    @return: A dictionary of dictionaries with stats for each server
    '''
    root = ET.parse(filename).getroot()
    server_stats = Ddict( dict )
    
    for subelement in root:

        server = subelement.find(SERVER).text
        uptime = float(subelement.find(END).text) - float(subelement.find(START).text)
        
        # Make uptime zero if less than 1 minute (server never connected)
        if uptime < 60:
            uptime = 0
            
        error = subelement.find(ERROR).text
        time_diff = time.time() - float(subelement.find(END).text)
                
        weight = math.exp(-time_diff / 100000000)
        
        server_stats[server]['server'] = server
        
        try:
            if uptime < server_stats[server]['min_uptime'] and uptime > 0:
                server_stats[server]['min_uptime'] = uptime
            if uptime > server_stats[server]['max_uptime']:
                server_stats[server]['max_uptime'] = uptime
        except:
            server_stats[server]['min_uptime'] = uptime
            server_stats[server]['max_uptime'] = uptime
            
        # Tally the total starts and total runtime for each server
        # Skip this if uptime is zero
        if uptime > 0:
            try:
                server_stats[server]['server_starts'] += 1
                server_stats[server]['total_uptime'] += uptime
                server_stats[server]['numerator'] += uptime * weight
                server_stats[server]['denominator'] += weight
                server_stats[server]['failed_attempts'] += 0
            except:
                server_stats[server]['server_starts'] = 1
                server_stats[server]['total_uptime'] = uptime
                server_stats[server]['numerator'] = uptime * weight
                server_stats[server]['denominator'] = weight
                server_stats[server]['failed_attempts'] = 0
        else:
            try:
                server_stats[server]['server_starts'] += 1
                server_stats[server]['failed_attempts'] += 1
                server_stats[server]['numerator'] += 0
                server_stats[server]['denominator'] += 0
            except:
                server_stats[server]['server_starts'] = 1
                server_stats[server]['failed_attempts'] = 1
                server_stats[server]['numerator'] = 0
                server_stats[server]['denominator'] = 0
                                 
        # Calculte the average uptime for the server
        #server_stats[server]['average_uptime'] = server_stats[server]['total_uptime'] / server_stats[server]['count']
        # Tally number of errors encountered for each server
        if error != 'Server Switch' and uptime > 0:
            try:
                server_stats[server][ERROR] += 1
                server_stats[server]['weighted_error'] += weight
            except:
                server_stats[server][ERROR] = 1
                server_stats[server]['weighted_error'] = weight
        else:
            try:
                server_stats[server][ERROR] += 0
                server_stats[server]['weighted_error'] += 0
            except:
                server_stats[server][ERROR] = 0
                server_stats[server]['weighted_error'] = 0
    
    for server in server_stats:
        if server_stats[server]['denominator'] == 0:
            average_uptime = 0
        else:
            average_uptime = server_stats[server]['numerator'] / server_stats[server]['denominator']
            
        error = server_stats[server]['weighted_error']
        starts = server_stats[server]['server_starts']
        failed = server_stats[server]['failed_attempts']
        
        try: # % of failed connection attempts
            f_ratio = 1 - (failed / starts)
        except:
            f_ratio = 1
        
        try: # % of server connectinos that have disconnects
            e_ratio = 1 - (error / starts)
        except:
            e_ratio = 1
            
        server_stats[server]['weighted_uptime'] = average_uptime
        server_stats[server][SCORE] = average_uptime * e_ratio * f_ratio
        # cleanup
        del server_stats[server]['numerator']
        del server_stats[server]['denominator']
        
    return server_stats

def main():
    stats = calc_stats('vpnlog.xml')
    logging.write_xml(stats,'stats.xml', 'write')
        
main()